/*
 *  Copyright (C) 2025 ameise <ameise.wang@gmail.com> - All Rights Reserved
 *
 *  This file is part of e3net.
 *
 *  e3net is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  e3net is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with e3net. If not, see <https://www.gnu.org/licenses/>.
 */

package e3net

import (
	"net"
	"runtime"
	"sync"
	"sync/atomic"

	"gitee.com/ameise84/e3net/internal"
	"gitee.com/ameise84/e3net/internal/common"
	"gitee.com/ameise84/e3pool/go_pool"
)

func createStdTcpAcceptor(tag Tag, addr string, opts *TcpAcceptorOptions, hand AcceptHandler) (*stdTcpAcceptor, error) {
	network := string(internal.TCP)

	ls, err := createStdTcpListener(network, addr, opts.reusePort)
	if err != nil {
		return nil, err
	}

	ln := &stdTcpAcceptor{
		tag:      tag,
		network:  network,
		addr:     ls.Addr().String(),
		opts:     opts,
		hand:     hand,
		connPool: newStdTcpConnPool(opts.connOpts),
		ln:       ls,
	}
	ln.runner = go_pool.NewGoRunner(ln, "tcp acceptor", go_pool.DefaultOptions().SetSimCount(1))
	return ln, nil
}

func destroyStdTcpAcceptor(ts *stdTcpAcceptor) {
	if ts.ln != nil {
		_ = ts.ln.Close()
		ts.ln = nil
	}
}

type stdTcpAcceptor struct {
	common.Service
	tag     Tag
	network string
	addr    string

	opts     *TcpAcceptorOptions
	hand     AcceptHandler
	connPool *stdTcpConnPool
	ln       net.Listener

	pauseStat atomic.Bool
	runner    go_pool.GoRunner
	connMap   sync.Map
}

func (ts *stdTcpAcceptor) E3LogMarshall() string {
	return "std tcp acceptor tag =" + ts.tag
}

func (ts *stdTcpAcceptor) OnPanic(err error) {
	_gLogger.Error("on panic").Object(ts).Err(err).Println()
}

func (ts *stdTcpAcceptor) start() error {
	return ts.Service.Start(func() error {
		if ts.ln == nil {
			ln, err := createStdTcpListener(ts.network, ts.addr, ts.opts.reusePort)
			if err != nil {
				return err
			}
			ts.ln = ln
		}
		return ts.runner.AsyncRun(ts.acceptLoop)
	})
}

func (ts *stdTcpAcceptor) stop() {
	stat := ts.pauseStat.Load()
	ts.pauseStat.Store(true)
	ts.Service.Stop(func() {
		_ = ts.ln.Close()
	})
	ts.runner.Wait()
	ts.ln = nil
	ts.pauseStat.Store(stat)
}

func (ts *stdTcpAcceptor) getTag() Tag {
	return ts.tag
}

func (ts *stdTcpAcceptor) isPause() bool {
	return ts.pauseStat.Load()
}

func (ts *stdTcpAcceptor) pauseAccept() {
	ts.pauseStat.Store(true)
}

func (ts *stdTcpAcceptor) resumeAccept() {
	ts.pauseStat.Store(false)
}

func (ts *stdTcpAcceptor) closeAllConn() {
	mp := make(map[uint32]*stdTcpConn)
	stat := ts.pauseStat.Load()
	ts.pauseStat.Store(true)
	ts.connMap.Range(func(key, value any) bool {
		c := value.(*stdTcpConn)
		mp[c.instId] = c
		return true
	})
	for _, conn := range mp {
		_ = conn.closeAndWaitRecv()
	}
	ts.pauseStat.Store(stat)
}

func (ts *stdTcpAcceptor) acceptLoop(...any) {
	for {
		fd, err := ts.ln.Accept()
		if err != nil {
			if ts.Service.IsStopped() {
				break
			}
			//logger.Error(errors.Wrap(err, "tcp server net accept"))
			runtime.Gosched()
			continue
		}

		if ts.isPause() {
			_ = fd.Close()
			continue
		}

		c := ts.connPool.take()
		c.active(ts, ts.tag, fd, ts.opts.readTimeout, ts.opts.writeTimeout)
		c.ctx = ts.hand.OnNewConnect(ts.tag, c)
		isRun := false
		if c.ctx != nil {
			isRun = c.run()
		}
		if isRun {
			ts.connMap.Store(c.instId, c)
		} else {
			if c.ctx != nil {
				ts.hand.OnClosedConnect(c.ctx)
				c.ctx = nil
			}
			c.inActive()
			_ = fd.Close()
			ts.connPool.free(c)
			c.ctx = nil
		}
	}
	ts.closeAllConn()
}

func (ts *stdTcpAcceptor) handleCloseConn(c *stdTcpConn) {
	ts.hand.OnClosedConnect(c.ctx)
	ts.connMap.Delete(c.instId)
	c.ctx = nil
	c.inActive()
}
